Source file: /~heha/basteln/PC/Programmiergeräte/Wild GIF12/mtp.zip/usb.cpp

#include "usb.h"
#include <avr/interrupt.h>
#include "MTP.h"	// MTP::devicestatus, MTP::extendedeventdata
// Media Transfer Protocol: "still image capture device class"
char ep0dat[8];
byte ep0len;

static byte usbCfg;
static const char E0_SSH=6;	// 64 Byte	CONTROL	I/O
static const char E1_SSH=6;	// 64 Byte	BULK	OUT (Maximum für Bulk)
static const char E2_SSH=6;	// 64 Byte	BULK	IN
static const char E3_SSH=5;	// 32 Byte	Interrupt IN

#define W(x) byte(x),byte((x)>>8)

static const PROGMEM byte DeviceDesc[]={
 18,		// bLength
 1,		// bDescType		Device
 W(0x0110),	// USB version supported
 0,		// bDeviceClass		See interface
 0,		// bDeviceSubclass
 0,		// bDeviceProtocol
 1<<E0_SSH,	// bMaxPacketSize0	64
 W(0x16C0),	// 			Voti
 W(0x27DF),	// 			MIDI+1 = PTP
 W(0x0100),	//
 1,		// iVendor
 2,		// iProduct
 0,		// iSerialNo
 1		// bNumConfig
};

static const PROGMEM byte ConfigDesc[39]={
 9,		// bLength
 2,		// bDescType		Config
 W(sizeof ConfigDesc),	// wTotalLength
 1,		// bNumInterfaces
 1,		// bConfigValue
 0,		// iConfig
 0x80,		// bmAttributes
 50,		// bMaxPower (100 mA)
// still image interface descriptor
 9,		// bLength
 4,		// bDescType		Iface
 0,		// bInterfaceNumber
 0,		// bAlternateSetting	(only one)
 3,		// bNumEndpoints	MUSS 3 sein!!
 6,		// bIfaceClass		image interface
 1,		// bIfaceSubclass	still image capture
 1,		// bIfaceProtocol	bulk-only protocol
 0,		// iIface
// ENDPOINT_DESCRIPTOR_1_OUT
 7,		// bLength
 5,		// bDescType		ENDPOINT
 0x01,		// bEndpointAddress	1 OUT
 0x02,		// bmAttributes		bulk
 W(1<<E1_SSH),	// wMaxPacketSize	64 (Maximum)
 0,		// bInterval
// ENDPOINT_DESCRIPTOR_2_IN
 7,		// bLength
 5,		// bDescType		ENDPOINT
 0x82,		// bEndpointAddress	2 IN
 0x02,		// bmAttributes		bulk
 W(1<<E2_SSH),	// wMaxPacketSize	64 (Maximum)
 0,		// bInterval
// ENDPOINT_DESCRIPTOR_3_IN
 7,		// bLength
 5,		// bDescType		ENDPOINT
 0x83,		// bEndpointAddress	3 IN
 0x03,		// bmAttributes		interrupt
 W(1<<E3_SSH),	// wMaxPacketSize	16
 100,		// bInterval
};
#undef W

extern "C" byte getbyte(/*const char*&Z*/) __attribute__((naked));
byte getbyte() { asm(
"	sbrs	ZH,7	\n"
"	 ld	r24,Z+	\n"
"	sbrc	ZH,7	\n"
"	 lpm	r24,Z+	\n"
"	ret		\n"
);}

#define getByte(addr) (__extension__({	\
 byte r;				\
 __asm__(				\
"	sbrs	ZH,7		\n"	\
"	 ld	%0,Z		\n"	\
"	sbrc	ZH,7		\n"	\
"	 lpm	%0,Z		\n"	\
        :"=r"(r):"z"((addr))		\
 );					\
 r;					\
}))

extern "C" word getutf8(/*const char*&Z*/) __attribute__((naked));
// PE: Z = Zeiger auf fehlerfreien UTF-8-String (nur BMP0) in RAM oder Flash
// PA: Z vorgerückt um 1..3 Bytes
//     R25:R24 = UTF-16-Zeichen
// VR: R18,R19
word getutf8() {asm(
"	rcall	getbyte	\n"
"	clr	r25	\n"
"	sbrs	r24,7	\n"
"	 rjmp	3f	\n"	// plain ASCII to UTF-16
"	ldi	r18,0	\n"	// prepare OR data
"	ldi	r19,0x1F\n"	// prepare mask
"	sbrs	r24,5	\n"
"	 rjmp	2f	\n"	// jump when 2 byte UTF-8
"	mov	r18,r0	\n"	// 1110 xxxx = 3 byte UTF-8
"	andi	r18,0x0F\n"	// 0000 xxxx
"	swap	r18	\n"	// xxxx 0000
"	rcall	getbyte	\n"	// 10xx xxxx
"	ldi	r19,0x3F\n"
"2:	mov	r25,r24	\n"	// 110x xxxx or 10xx xxxx
"	and	r25,r19	\n"	// 000x xxxx or 00xx xxxx
"	rcall	getbyte	\n"	// 10xx xxxx
"	lsl	r24	\n"	// 0xxx xxx0
"	lsl	r24	\n"	// xxxx xx00
"	lsr	r25	\n"	// 000x xxxx -> x
"	ror	r24	\n"	// xxxx xxx0
"	lsr	r25	\n"	// 0000 xxxx -> x
"	ror	r24	\n"	// xxxx xxxx
"	or	r25,r18	\n"	// apply OR data (!0 in case of 3 byte UTF-8)
"3:	ret		\n"
);}

// Macht aus UTF-8 einen USB-mäßigen String-Deskriptor
// Der String darf im RAM oder Flash liegen
static byte*makeStringDesc(const char*s) {
 static byte sbuf[32];
 asm(
"	movw	r0,XL	\n"	// save address
"	ldi	r25,3	\n"
"1:	st	X+,r24	\n"	// at first execution, fill with garbage
"	st	X+,r25	\n"	// at first execution, set "3" for type
"	rcall	getutf8	\n"
"	adiw	r24,0	\n"	// end of string?
"	brne	1b	\n"	// no, append character
"	mov	r24,XL	\n"	// save end address
"	movw	XL,r0	\n"	// restore start address
"	sub	r24,XL	\n"	// calculate byte length
"	st	X,r24	\n"	// set length byte
"	clr	r1	\n"
 :"+z"(s),"=memory"(sbuf):"x"(sbuf):"r24","r25","r18","r19");
 return sbuf;
}

void usbInit() {
 UHWCON=1;
 USBCON=0xB0;	// Ubus aktivieren, aber ohne Takt
 USBINT=1;	// beim nächsten usbPoll USB aktivieren oder deaktivieren lassen
}	// Dokumentationsfehler: USBCON.7 muss 1 sein damit UDCON.0 0 bleibt!

static void usbPollEp0() {
 UENUM=0;		// Endpoint 0 betreffend
 if (UEINTX&4) {	// OUT angekommen?
  ep0len=0;
  while (UEINTX&0x20) ep0dat[ep0len++]=UEDATX;	// Bytes einsammeln
  UEINTX=~4;
  handleEp0Out();
 }
 if (UEINTX&8) {	// SETUP angekommen?
  byte len;
  const void*addr;
  byte bmRequestType __attribute__((unused)) =UEDATX;
  byte bRequest=UEDATX;
  byte wValueL=UEDATX;
  byte wValueH=UEDATX;
  byte wIndexL __attribute__((unused)) =UEDATX;
  byte wIndexH __attribute__((unused)) =UEDATX;
  byte wLengthL=UEDATX;
  byte wLengthH=UEDATX;
  UEINTX=~8;		// Jetzt erst(!) alles löschen außer TXINI
// Der Einfachheit halber (Platzmangel!!) ohne bmRequestType auszuwerten,
// ohne String-Deskriptoren
//  wLength=wLengthH<<8|wLengthL;
  switch (bRequest) {
// Standard-Requests
   case 0: {			// Get Status
    UEDATX=0;			// Bus Powered (Device)
    UEDATX=0;			// 0 (Configuration)
   }goto okay;			// Kein HALT (Endpoint)
   case 1:			// Clear Feature
   case 3: goto okay;		// Set Feature
   case 5: {			// Set Address (wValueL)
    UEINTX=0;			// mit 0-Byte-IN-Paket beantworten
    UDADDR=wValueL;		// laut Datenblatt, praktisch nicht erforderlich
    while (!(UEINTX&1));	// warten bis abgeschickt
    UDADDR=wValueL|0x80;
   }return;
   case 6: switch (wValueH) {	// Get Descriptor
    case 1: addr=FP(DeviceDesc); break;
    case 2: addr=FP(ConfigDesc); len=sizeof ConfigDesc; goto gd2;
    case 3: switch (wValueL) {
     case 0: addr=makeStringDesc(F("\xD0\x89")); break;	// english-US
     case 1: addr=makeStringDesc(F("Haftmann")); break;
     case 2: addr=makeStringDesc(F("GIF reader")); break;
     default: goto stall;
    }break;
    default: goto stall;
   }
   len=getByte(addr);
gd2:
   if (!wLengthH && len>wLengthL) len=wLengthL;	// Nur soviel der Host will
   while (len) {
    byte n;
    while (!((n=UEINTX)&0x0D));// warten bis Interrupt
    if (n&0x0C) return;	// Ende bei (unerwartetem) OUT- oder SETUP-Paket (15h kommt vorbei!)
    n=1<<E0_SSH; if (n>len) n=len;
    len-=n;
    asm(	//  do UEDATX=getByte(addr++); while (--n);
"1:	rcall	getbyte	\n"	// Von-Neumannisierte Adresse lesen
"	sts	%1,r24	\n"
"	dec	%2	\n"
"	brne	1b	\n"
:"+z"(addr):"m"(UEDATX),"r"(n):"r24");
    UEINTX&=~1;
   }return;
   case 8: {		// Get Configuration
    UEDATX=usbCfg&1;
   }goto okay;
   case 9: if (wValueL<2) {	// Set Configuration
    if (usbCfg=wValueL) {	// alles einfach gepuffert (reicht hier)
     UENUM=1; UECONX=1; UECFG0X=0x80; UECFG1X=2|E1_SSH-3<<4;	// EP1OUT 64 Byte
     UENUM=2; UECONX=1; UECFG0X=0x81; UECFG1X=2|E2_SSH-3<<4;	// EP2IN  64 Byte
     UENUM=3; UECONX=1; UECFG0X=0xC1; UECFG1X=2|E3_SSH-3<<4;	// EP3IN  32 Byte
     UENUM=0;
    }else{
     UERST=0x0E; 
     UERST=0;	// alle 3 Endpoints rücksetzen
    }
//    bulkoutlen=0;
    goto okay;
   }break;
   case 10: UEDATX=0; /*nobreak*/;	// Get Interface (= Alternate Setting)
   case 11: okay:UEINTX=~1; return;	// Set Interface (ignorieren)
// class specific requests
   case 100: return;		// cancel set-up (00100001, wLength=6)
   case 101: {			// get extended event data (10100001, wLength=bufsize);
    addr=MTP::extendedeventdata; len=8;
   }goto gd2;
   case 102: goto okay;		// device reset set-up (00100001, wLength=0)
   case 103: {			// get device status (10100001, wLength=bufsize)
    addr=&MTP::devicestatus; len=MTP::devicestatus.wLength;
   }goto gd2;
  }
stall:UECONX=0x21;		// Alles andere: STALL aktivieren
 }
}

bool usbRecvPoll() {
 UENUM=1;
 return UEINTX&0x80;
}

// Zwangsweise USB-Daten lesen
byte usbRecv(void*buf,byte exlen) {
 byte*p=(byte*)buf;
 byte len=0;
 UENUM=1;
 while (len<exlen) {
  while (!(UEINTX&0x20)) {	// BUGBUG: Paket könnte inzwischen ankommen
   UEINTX=0;			// Datenblock zurück zur SIE
   while (!(UEINTX&0x80));	// Warten auf weitere Daten (unendlich)
  }
  byte b=UEDATX;
  if (p) *p++=b;		// RAM schreiben
  len++;
 }
 if (!(UEINTX&0x20)) UEINTX=0;	// ausgelesen: zurück zur SIE
 return len;
}

void usbPoll() {
 if (USBINT&1) {	// Ab- oder Anstecken des Hosts?
  USBINT=0;
  if (USBSTA&1) {	// Angesteckt
   UDCON=0;		// Pullup aktivieren
  }else{		// Abgesteckt
   UDCON=1;		// Pullup deaktivieren
   USBCON=0xB0;		// Takt deaktivieren
   usbCfg=0;
  }
 }
 byte udint=UDINT;
 UDINT=0;
 if (udint&0x10 && USBCON&0x20) {	// Aktivität && Takt eingefroren?
#if F_CPU==16000000
  PLLCSR=0x12;		// PLL aktivieren (für 16-MHz-Quarz)
#elif F_CPU==8000000
  PLLCSR=0x02;		// PLL aktivieren (für 8-MHz-Quarz)
#else
# error Symbol "F_CPU" muss 8 oder 16 MHz ergeben!
#endif
  while(!(PLLCSR&1));	// warten bis eingerastet
  USBCON=0x90;		// Takt aktivieren
 }
 if (udint&1) {		// USB-Suspend
  USBCON=0xB0;		// Takt deaktivieren
  PLLCSR=0;		// PLL deaktivieren
 }
 if (udint&8) {		// USB-Reset-Ende?
  usbCfg=0;		// Nicht konfiguriert (Adresse wird von Hardware zurückgesetzt)
  UENUM=0;
  UECONX=1;		// Endpoint 0 aktivieren
  UECFG1X=2|E0_SSH-3<<4;// 8 Bytes für Endpoint 0 im DPRAM reservieren
 }
 usbPollEp0();
}

void usbSend(const void*buf,byte len,bool flush,byte ep) {
 const byte*p=(const byte*)buf;
 do{
  while (UENUM=ep,!(UEINTX&0x80)) usbPoll();	// Warten bis Puffer frei
  while (UEINTX&0x20 && len) {
#if 0
   UEDATX=(int)p<0?pgm_read_byte(p++):*p++;	// aus RAM oder Flash
#else
   asm(" rcall getbyte\n sts %1,r24 \n":"+z"(p):"m"(UEDATX):"r24");
#endif
   --len;
  }
  if (flush || !(UEINTX&0x20)) UEINTX=0;	// Puffer abschicken
 }while(len);
}


void usbPurge() {
 UENUM=1;			// OUT-Puffer leeren
 while (UEINTX&0x80) UEINTX=0;
 UENUM=2;
 while (UESTA0X&0x03) {
  UEINTX=0x05;			// Kill Bank IN
  while (UEINTX&0x04);		// warte bis fertig
 }
 UERST=0x04;
 UERST=0;			// Keine Daten, die auf Abholung warten
}
Detected encoding: UTF-80